package sqlinsert

import (
	"github.com/pkg/errors"
	"reflect"
	"strings"
)

// 预定义错误
var (
	errInvalidEntity  = errors.New("invalid entity")
	errEmptyTableName = errors.New("empty table name")
)

type UserModel struct {
	ID         uint32      `db:"id" primary:"true"`
	Mobile     string      `db:"mobile"`
	Name       string      `db:"name"`
	Age        int32       `db:"age"`
	Username   string      `db:"username"`
	BaseEntity *BaseEntity `db:"-"`
}

type StudentModel struct {
	ID         uint32     `db:"id" primary:"true"`
	Name       string     `db:"name"`
	Birthday   string     `db:"birthday"`
	BaseEntity BaseEntity `db:"-"`
}

func (this StudentModel) Table() string {
	return "student"
}

type SimplePerson struct {
	ID   int64  `db:"id" primary:"true"`
	Name string `db:"name"`
	Age  int32  `db:"age"`
}

func (this SimplePerson) Table() string {
	return "simple_person"
}

type BaseEntity struct {
	CreateTime int64 `db:"create_time"`
	UpdateTime int64 `db:"update_time"`
}

func (this UserModel) Table() string {
	return "user"
}

func getTableName(v reflect.Value) string {

	if v.Kind() != reflect.Struct {
		return ""
	}

	if fn := v.MethodByName("Table"); fn.IsValid() {
		return fn.Call(nil)[0].String()
	}

	return ""
}

// GenerateSmt 获取sql语句,根据db标签获取字段名
func GenerateSmt(data interface{}) (sqlstr string, values []interface{}, err error) {
	if data == nil {
		err = errInvalidEntity
		return
	}

	v := reflect.ValueOf(data)

	// 表名
	tableName := getTableName(v)

	for v.Kind() == reflect.Ptr {
		v = v.Elem()
	}

	if v.Kind() != reflect.Struct {
		err = errInvalidEntity
		return
	}

	if !v.IsValid() {
		err = errInvalidEntity
		return
	}

	if v.IsZero() {
		err = errInvalidEntity
		return
	}

	if tableName == "" {
		err = errEmptyTableName
		return
	}

	// 判断是否为插入语句
	var isInsert = true

	var updateSql, whereSql string

	var insertSql, valueSql string
	valueSql = " VALUES ("

	updateSql = "UPDATE `" + tableName + "` SET "
	insertSql = "INSERT INTO `" + tableName + "` ("

	var primaryValue interface{}

	// 处理字段
	var checkField func(reflect.Value)

	checkField = func(v reflect.Value) {
		if v.Kind() != reflect.Struct {
			return
		}

		vt := v.Type()

		for i := 0; i < vt.NumField(); i++ {
			field := vt.Field(i)
			vf := v.Field(i)

			for vf.Kind() == reflect.Ptr {
				vf = vf.Elem()
			}

			if field.PkgPath != "" {
				continue
			}

			// 如果当前节点是结构体，则递归处理
			if vf.Kind() == reflect.Struct {
				checkField(vf)
				continue
			}

			// 获取字段名
			column := field.Tag.Get("db")

			if column == "" || column == "-" {
				continue
			}

			// 判断是否为主键
			if field.Tag.Get("primary") == "true" {
				if vf.IsValid() {
					if !vf.IsZero() {
						isInsert = false
						whereSql = " WHERE `" + column + "` = ?"
						primaryValue = vf.Interface()
					}
				}

				continue
			}

			if isInsert {
				insertSql += "`" + column + "`" + ", "
				valueSql += "?, "
			} else {
				updateSql += "`" + column + "`" + " = ?, "
			}

			values = append(values, vf.Interface())
			continue

		}

	}

	checkField(v)

	valueSql = strings.TrimRight(valueSql, " ")
	insertSql = strings.TrimRight(insertSql, " ")
	updateSql = strings.TrimRight(updateSql, " ")

	if isInsert {
		insertSql = insertSql[:len(insertSql)-1] + ")" + valueSql[:len(valueSql)-1] + ")"
	} else {
		updateSql = updateSql[:len(updateSql)-1] + whereSql
		values = append(values, primaryValue)
	}

	sqlstr = insertSql

	if !isInsert {
		sqlstr = updateSql
	}

	return
}
